package com.bckj.fastboot.flowable.proc.operation;

import com.bckj.fastboot.core.lang.BasicContext;
import com.bckj.fastboot.core.operation.Operation;
import com.bckj.fastboot.core.operation.context.OpContext;
import com.bckj.fastboot.flowable.model.ProcElement;
import com.bckj.fastboot.flowable.model.ProcGateway;
import com.bckj.fastboot.flowable.util.ProcUtils;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.SequenceFlow;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.util.List;

@Component
public class ProcBuildOperation implements Operation {

    public void execute(OpContext context) {
        var ctx = (ProcBuildContext) context;
        var procModel = ctx.getProcModel();
        Assert.hasText(procModel.getProcessId(), "流程ID不能为空");
        Assert.hasText(procModel.getProcessName(), "流程名称不能为空");
        Process process = new Process();
        process.setId(procModel.getProcessId());
        process.setName(procModel.getProcessName());
        ctx.setProcess(process);
        List<ProcElement> elements = procModel.getElements();
        for (var element : elements) {
            buildNode(ctx, element);
        }
    }

    private void buildNode(ProcBuildContext ctx, ProcElement element) {
        Process process = ctx.getProcess();
        if (element instanceof ProcGateway gateway) {
            Assert.notEmpty(gateway.getBranches(), "网关分支不能为空");
            FlowElement gatewayStart = ProcUtils.convert(element);
            process.addFlowElement(gatewayStart);
            addSequenceFlow(process, ctx.getLastId(), gatewayStart.getId(), null);
            String gatewayEndId = gateway.getBizId() + "_end";
            for (var branch : gateway.getBranches()) {
                ctx.setLastId(null);
                Assert.notEmpty(branch.getElements(), "分支节点不能为空");
                addSequenceFlow(process, gatewayStart.getId(), branch.getElements().get(0).getBizId(), branch.getConditionExpression());
                for (ProcElement e : branch.getElements()) {
                    buildNode(ctx, e);
                }
                addSequenceFlow(process, branch.getElements().get(branch.getElements().size() - 1).getBizId(), gatewayEndId, null);
            }
            FlowElement gatewayEnd = gatewayStart.clone();
            gatewayEnd.setId(gatewayEndId);
            process.addFlowElement(gatewayEnd);
            ctx.setLastId(gatewayEnd.getId());
        } else {
            FlowElement flowElement = ProcUtils.convert(element);
            process.addFlowElement(flowElement);
            if (ctx.getLastId() != null) {
                addSequenceFlow(process, ctx.getLastId(), flowElement.getId(), null);
            }
            ctx.setLastId(flowElement.getId());
        }
    }

    private void addSequenceFlow(Process process, String sourceId, String targetId, String conditionExpression) {
        process.addFlowElement(buildSequenceFlow(sourceId, targetId, conditionExpression));
    }

    private SequenceFlow buildSequenceFlow(String sourceId, String targetId, String conditionExpression) {
        SequenceFlow sequenceFlow = new SequenceFlow();
        sequenceFlow.setSourceRef(sourceId);
        sequenceFlow.setTargetRef(targetId);
        sequenceFlow.setConditionExpression(conditionExpression);
        return sequenceFlow;
    }
}
